/**
 * File: Ship.cs
 * Date: 12-02-2008
 * Auth: Jan-Jaap Severs
 * Desc: A basic ship class.
 */

#region Using statements

using System;
using System.Collections.Generic;
using System.Text;
using Gremlin.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Squadron.Networking;

#endregion

namespace Squadron
{
    public class Ship : Mesh
    {
        #region Member variables

        private UInt16 id = 0;

        private float rotationSpeed = 0.1f;
        private float currentRoll = 0.0f;
        private float rollCorrection = 0.0075f;
        private float maxRoll = 0.75f;

        private float forwardSpeed = 0.3f;

        private Vector3 right = Vector3.Right;
        private Vector3 direction = Vector3.Forward;
        private Vector3 up = Vector3.Up;

        private bool OrientationOverride = false;

        private ContentManager contentManager;

        private Gun gun;

        private int life = 100;

        #endregion

        public Ship(Microsoft.Xna.Framework.Game game, ContentManager content, string modelName) 
            : base(game, content, modelName)
        {
            contentManager = content;
            gun = new MachineGun(game, content, "Models\\p1_mgun", "Models\\BulletTest", 1, 100);

            _boundingSphere.Radius = 2.0f; 
        }

        public override void Update(GameTime gameTime)
        {
            // Give the ship a constant thrust forward.
            Move(direction * forwardSpeed);

            if (!OrientationOverride)
            {
                // Reconstruct the ship's world matrix based on the direction, local up and right vectors.
                _world = Matrix.Identity;

                _world.Forward = direction;
                _world.Up = up;
                _world.Right = right;
                _world.Translation = _position;
                _world = Matrix.CreateScale(_scale) * Matrix.CreateRotationZ(currentRoll) * _world;

                // Decompose the world matrix back into the seperate components, so the various accessors
                // in GameObject still work.
                _world.Decompose(out _scale, out _rotation, out _position);
            }
            else
            {
                _world = Matrix.CreateScale(_scale) * Matrix.CreateFromQuaternion(_rotation) * Matrix.CreateTranslation(_position);
                //OrientationOverride = false;
            }

            if (currentRoll > 0.0f)
                currentRoll -= rollCorrection;
            else if (currentRoll < 0.0f)
                currentRoll += rollCorrection;

            gun.Position = _position;
            gun.Rotation = _rotation;
            gun.Scale = _scale;

            gun.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// Called when the ship has been hit by a projectile.
        /// </summary>
        /// <param name="proj">The projectile that has hit the ship.</param>
        public void TakeDamage(Projectile proj)
        {
            life -= (int)proj.Damage;
            proj.IsDead = true;
        }

        /// <summary>
        /// Polls the keyboard or gamepad for player input and controls the ship.
        /// </summary>
        public void UpdatePlayerInput(ClientPacket clientPacket)
        {
            // Retrieve the current keyboard input.
            KeyboardState currentState = Keyboard.GetState();

            float deltaX = clientPacket.YawPitch.X, deltaY = clientPacket.YawPitch.Y;
            
            if(deltaY < 0.0f && currentRoll > -maxRoll)
                currentRoll += (deltaY * 2);
            else if(deltaY > 0.0f && currentRoll < maxRoll)
                currentRoll += (deltaY * 2);

            bool fireGuns = clientPacket.FireGuns;

            // Create rotation matrix from rotation amount
            Matrix rotationMatrix =
                Matrix.CreateFromAxisAngle(right, deltaX) *
                Matrix.CreateRotationY(deltaY);
               
            // Rotate orientation vectors
            direction = Vector3.TransformNormal(direction, rotationMatrix);
            up = Vector3.TransformNormal(up, rotationMatrix);

            // Re-normalize orientation vectors
            // Without this, the matrix transformations may introduce small rounding
            // errors which add up over time and could destabilize the ship.
            direction.Normalize();
            up.Normalize();

            // Re-calculate Right
            right = Vector3.Cross(direction, up);

            // The same instability may cause the 3 orientation vectors may
            // also diverge. Either the Up or Direction vector needs to be
            // re-computed with a cross product to ensure orthagonality
            up = Vector3.Cross(right, direction);            
        }

        public Projectile Fire()
        {
            return gun.Fire(_rotation);
        }

        public UInt16 ID
        {
            get { return this.id; }
            set { this.id = value; }
        }

        public override Quaternion Rotation
        {
            get
            {
                return base.Rotation;
            }
            set
            {
                base.Rotation = value;
                OrientationOverride = true;
            }
        }

        public int Life
        {
            get { return life; }
        }
    }
}
